using System;
using System.Collections.Generic;
using System.Text;
using PostSharp.Laos;

using Spring.Context.Support;
using Spring.Context;
using System.Diagnostics;
using System.Reflection;
using System.Web;

namespace B4ALL.Cache
{

    [Serializable]
    public class CacheableAttribute : OnMethodBoundaryAspect
    {
        private const string CACHE_SERVICE = "cacheService";

        /// <summary>
        /// Tests if method returns value is already in cache. Otherwise, lets the method execute.
        /// </summary>
        /// <param name="eventArgs"></param>
        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
            IApplicationContext l_ctx = ContextRegistry.GetContext();
            if (l_ctx.ContainsObject(CACHE_SERVICE))
            {
                ICacheService l_cacheService = (ICacheService)l_ctx.GetObject(CACHE_SERVICE);
                l_cacheService.OnEntry(eventArgs, this);
            }
        }

        /// <summary>
        /// Method return value is insert in cache manager
        /// </summary>
        /// <param name="eventArgs"></param>
        public override void OnSuccess(MethodExecutionEventArgs eventArgs)
        {
            IApplicationContext l_ctx = ContextRegistry.GetContext();
            if (l_ctx.ContainsObject(CACHE_SERVICE))
            {
                ICacheService l_cacheService = (ICacheService)l_ctx.GetObject(CACHE_SERVICE);
                l_cacheService.OnSuccess(eventArgs, this);
            }
        }

        /// <summary>
        /// Compile time validation of methods on which attribute is applied.
        /// </summary>
        /// <param name="method"></param>
        /// <returns></returns>
        public override bool CompileTimeValidate(System.Reflection.MethodBase method)
        {

            System.Reflection.MethodInfo mInfo = (System.Reflection.MethodInfo)method;

            if (method.IsConstructor || method.MemberType != System.Reflection.MemberTypes.Method)
            {
                throw new Exception(String.Format("Constructors, and members other than methods are not cacheable Class: {0}, Method: {1}", mInfo.ReflectedType.FullName, mInfo.Name));
                return false;
            }
            else
            {
                if (mInfo.ReturnType.Name.ToLower() == "void")
                {
                    throw new Exception(String.Format("Void methods are not cacheable. Class: {0}, Method: {1}", mInfo.DeclaringType.FullName, mInfo.Name));
                    return false;
                }
                else
                {
                    foreach (System.Reflection.ParameterInfo paramInfo in method.GetParameters())
                    {
                        if (paramInfo.IsOut)
                        {
                            throw new Exception("Out-parameters are not cacheable");
                            return false;
                        }
                    }
                }
            }
            return true;
        }

    }
}
